Loaded packages

# Load packages
require(RPostgreSQL)
require(getPass)
library(tidyverse)
library(sf)
library(lubridate)
library(zoo) # to extend the apply family of functions (rolling window)

Preprocessing / R functions

# If data preprocessing is needed, do it here.
# functions:
flagR <- function(x) { # function to flag values in quality check 1
  ifelse(x>=1, 1,0)    # sets flag to 0 when passed, to 1 when failed
}

flagP <- function(x){  # function flags values in quality check 3 (variability)
  ifelse(length(unique(x)) == 1, 1,0)  # no variability -> 1 unique val
}                      # must be applied using a rolling window 

1. Hobo meta informations

Hobo picture and description

image of my Hobo

2. Consistent HOBO data file

Read in from GitHub

# Load data from Github (then eval = TRUE)
my_HOBO <- read.csv("https://raw.githubusercontent.com/data-hydenv/data/master/hobo/2023/raw/10350049.csv", skip = 1)
# Load calibration file from GitHub
t_cal <- read.csv("https://raw.githubusercontent.com/data-hydenv/data/master/hobo/2023/calibration.csv")

Date/Time column must be parsed properly:

my_HOBO$Datum.Zeit..GMT.01.00 <- mdy_hms(my_HOBO$Datum.Zeit..GMT.01.00)

Tidy up and rename the columns

my_HOBO <- as_tibble(my_HOBO) %>% 
  select(., 1:4) %>% 
  rename(id = 1, dttm = 2, temp = 3, lux = 4)

head(my_HOBO)

2.1. Calibration

The calibration data from GitHub is a table of the time when the calibration measurement was performed and the target value temperature.

caltime <- t_cal$This.is.the.calibration.value[3] # time of calibration

calib_line <- which(my_HOBO$dttm == caltime) # according line in raw data

meas_temp <- num(my_HOBO[calib_line,]$temp, digits = 3) # what the HOBO measured

meas_temp <- meas_temp[1] # as non-numbered number

tru_temp <- num(as.numeric(t_cal$to.calibrate.your.Hobo.measurements.in..C.[3]),
                digits = 3) # what the target value is

tru_temp <- tru_temp[1] # target value as non-numbered number

cal_offset <- meas_temp-tru_temp # difference of the two

cal_offset <- as.numeric(cal_offset[1])

# so -0.267 is now my calibration offset

my_HOBO$temp <- my_HOBO$temp-cal_offset # subtract offset from raw temperature

2.2. create HOBO data file

Truncate data to the given time frame. First declare the target start and end time using ´lubridate::ymd_hms´.

start_time <- ymd_hms("2022-12-01 00:00:00") # start point
end_time <- ymd_hms("2023-01-07 23:50:00") # end point
time_range <- interval(start_time, end_time) # may be useful later

Filter the raw data to clip it according to the start and end times and also overwrite the id column with a new value starting from 1. format() was used to format decimals in the light intensity and temperature vectors and to change the timestamp format.

my_HOBO <- my_HOBO %>% 
  filter(between(dttm, start_time, end_time)) # filter 
my_HOBO <- my_HOBO %>% 
  mutate(., id = c(1:length(my_HOBO$id))) # and fix ID col

my_HOBO$temp <-  format(my_HOBO$temp, digits=3, nsmall=3) # fix decimals in temp
my_HOBO$lux <-  format(my_HOBO$lux, digits=3, nsmall=3) # and in light intensity

my_HOBO$dttm <- format(my_HOBO$dttm, "%Y-%m-%d %H:%M") # timestamp format

The tibble: my_HOBO was then written to disk and uploaded to GitHub.

# write to file
write_csv(my_HOBO, file = "10350049.csv")

2.3. verify your file

3. Quality control

Read the data back in from GitHub…

# Load data from Github (then eval = TRUE)
HOBO_qc <- read.csv("https://raw.githubusercontent.com/data-hydenv/data/master/hobo/2023/10_minutes/10350049.csv")
head(data)
                                                                            
1 function (..., list = character(), package = NULL, lib.loc = NULL,        
2     verbose = getOption("verbose"), envir = .GlobalEnv, overwrite = TRUE) 
3 {                                                                         
4     fileExt <- function(x) {                                              
5         db <- grepl("\\\\.[^.]+\\\\.(gz|bz2|xz)$", x)                     
6         ans <- sub(".*\\\\.", "", x)                                      

3.1. Quality control procedures (QCPs)

3.1.1. Measurement range (Plausible values)

…and check both value ranges.

# TODO NAs
range(HOBO_qc$temp)
[1] -8.714 21.397
range(HOBO_qc$lux)
[1]    0.0 2755.6

Question: How many data points are outside the measurement range?

Answer: None

3.1.2. Plausible rate of change

# lag one column, then mutate difference to new column
HOBO_wip <- HOBO_qc %>% 
  mutate(., lagged=lag(temp)) %>% 
  mutate(., delta=temp-lagged)

range(na.omit(HOBO_wip$delta)) # delta values out of legal range?
[1] -1.528  2.906

Question: Describe shortly how many data points failed during this QCP and discuss whether there is a certain daytime pattern of failure or not?

Answer:

qc_R <- sapply(HOBO_wip$delta,flagR) # apply flagging function 

HOBO_wip <- cbind(HOBO_wip,qc_R) # collect results in new data frame

length(which(qc_R==1)) # count occurence of bad data points
[1] 24
# TODO day/night pattern?

3.1.3. Minimum variability (Persistence)

Use zoo::rollapply() to run the persistence check over a moving time window of 6 time steps (i.e. 60 minutes).

qc2 <- rollapply(HOBO_wip$delta, width=6,FUN=flagP) # width parameter is window

Task: Code in this section should analyse the persistence.

# TODO

Compile results

qc_P <- c(1:length(HOBO_wip$delta)) # vector to store persistence flag
qc_P[1:5] <- NA # manually fill positions 1 through 5
qc_P[6:length(qc_P)] <- qc2 # append results
HOBO_wip <- cbind(HOBO_wip,qc_P) # collect in tibble

3.1.4. Light intensity

The HOBO was situated in a planter at all times so there was no disturbance of the light measurement.

hist(HOBO_wip$lux, breaks = 25)

summary(HOBO_wip$lux) # quite the spread
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.0     0.0     0.0   114.0    86.1  2755.6 
sd(HOBO_wip$lux)
[1] 283.974
toplux <- filter(HOBO_wip,lux<=500 & lux>1)
hist(toplux$lux,breaks=50)

Assign SICs from SOURCE!!! to light measurement data points

HOBO_wip <- HOBO_wip %>% 
  mutate(SIC = case_when(lux <= 10 ~ 'Night_0',
                         lux <= 500 ~ 'Rise_Set_1',
                         lux <= 2000 ~ 'Overcast_full_2',
                         lux <= 15000 ~ 'Overcast_light_3',
                         lux <= 20000 ~ 'Clear_4',
                         lux < 50000 ~ 'Sunshine_5',
                         lux >= 50000 ~ 'Brightshine_6',
                         ))

unique(HOBO_wip$SIC) # only the first four SICs present
[1] "Night_0"          "Rise_Set_1"       "Overcast_full_2" 
[4] "Overcast_light_3"

Task: Discuss shortly how often and when during daytime the QCP4 flags bad data. Elaborate on some reasons for your results.

Answer:

# analyse occurence of flags and aggregate qc fails 
HOBO_wip <- HOBO_wip %>% 
  mutate(qc_tot = qc_P + qc_R)

HOBO_wip$qc_tot[1:5] <- 0


HOBO_wip <- HOBO_wip %>% 
  mutate(qc_all = case_when(qc_tot == 0 ~ 0,
                   qc_tot != 0 ~ 1))

# as percentage

qc_result <- table(HOBO_wip$qc_all)

qc_result

   0    1 
5269  203 
num(((qc_result[2]/length(HOBO_wip$qc_all))*100),digits=4)
<pillar_num:.4![1]>
     1 
3.7098 
# TODO
# Present a table or graph to show how many data points fail during the four specific QCPs. Discuss shortly
# the reasons for failure and compare the different QCPs against each other.
# create qc_df
# TODO 
qc_df <- as.tibble(HOBO_wip)

# numbering all hours
hour_ct <- rep(c(1:912), each = 6)
# stick it to the df
qc_df <- cbind(qc_df, h_ct = hour_ct)

# qc_df <- qc_df %>% 
#   mutate(flag_)

table(qc_df$qc_tot)

   0    1 
5269  203 
# I have 203 cases but each one has failed only one of the quality checks

test <- qc_df %>% 
  group_by(., h_flag = h_ct) %>% 
  summarize(., sum_flags=sum(qc_all)) %>% 
  filter(sum_flags>=2)
# this yields a vector of all hours that must be set to NA
bad_hours <- test$h_flag

qc_df2 <- qc_df

bad_temps <- which(qc_df2$h_ct %in% bad_hours)
qc_df2$temp[bad_temps] <- NA

Share of NAs

# Calculate the share of NAs in your hourly time-series in % and write it into the
# meta table (sheet: “Quality


hrly_dat <- qc_df2 %>% 
  group_by(., h_ct) %>% 
  summarise(., mean_temp=mean(temp))
# this yields hourly means with NAs 
# share of NA in hourly time series

table(is.na(hrly_dat$mean_temp))

FALSE  TRUE 
  862    50 
# 50 NAs

num((50/length(hrly_dat$mean_temp))*100,digits = 4)
<pillar_num:.4![1]>
[1] 5.4825

3.2. Summarize

# code for summarizing here

Task: Present a table or graph to show how many data points fail during the four specific QCPs. Discuss shortly the reasons for failure and compare the different QCPs against each other.

Answer:

Task: At the end of the code section above you should generate one! tibble or data.frame named qc_df with all time information, all data points (temperature and lux) and your outcomes of the different QCPs.

Answer:

3.3. Aggregate

# code for aggregation here

Task: At the end of the code section above you should generate one! tibble or data.frame named hobo_hourly with averaged temperature values per hour or NA values (if the hour is flagged as bad data). See exercise description for more details.

dttm <- seq(start_time, end_time, by= "hours")

hobo_hourly <- hrly_dat %>% 
  select(th=mean_temp) %>% 
  mutate(date_time=dttm) %>% 
  mutate(origin=rep("H"))

hobo_hourly$date_time <- format(hobo_hourly$date_time, "%Y-%m-%d %H:%M:%S")
hobo_hourly$th <-  format(hobo_hourly$th, digits=3, nsmall=3)

write_csv(hobo_hourly, file = "10350049_Th.csv" )

4. Fill-up with reference station

4.1. reference station


# fill up all the NA in your
# hobo_hourly from chapter 3.3 based on a regression model between your station
# and a reference station. 

# WBI Station
# data import from hdd
WBI_raw <- read.csv("Stunde_096.csv", sep=";")
# convert to proper POSX or ld object
WBI_raw$Tag <- dmy(WBI_raw$Tag)
class(WBI_raw$Tag)
[1] "Date"
# works semi well with this timestamp...
WBI_raw$Stunde <- hm(WBI_raw$Stunde)
class(WBI_raw$Stunde)
[1] "Period"
attr(,"package")
[1] "lubridate"
# i'll make my own
# declare time frame
start_time <- ymd_hms("2022-12-01 00:00:00")
end_time <- ymd_hms("2023-01-07 23:50:00")
time_range <- interval(start_time, end_time)
# make my own dttm and scrap the ones that came with the WBI data
dttm <- seq(start_time, end_time, by= "hours")
# clip WBI data, add dttm and rename cols
Wdat_WBI <- WBI_raw %>% 
  filter(.,between(Tag, date(start_time), date(end_time))) %>% 
  select(.,temp=AVG_TA200) %>% 
  mutate(dttm=dttm)
# TODO format the timestamp to get rid of seconds eventually (if the assignment says so)


# FREIBURG Garten station
GAR_raw <- read.csv("Freiburg_Garten_2022-11-30_2023-01-08.csv")

# correct missing line in GAR_raw
GAR_raw <- GAR_raw %>% 
  add_row(UTC = "2022-12-16 10:00:00", Lokalzeit = "2022-12-16 11:00:00", Lufttemperatur...C. = NA, .before = 395)

GAR_raw[390:396,]
# that's better, now the whole converting and clipping procedure again

# GAR_raw$Lokalzeit <- ymd_hms(GAR_raw$Lokalzeit, tz="Europe/Berlin")
# GAR_raw$UTC <- ymd_hms(GAR_raw$UTC, tz="UTC")
# class(GAR_raw$Lokalzeit)
# class(GAR_raw$UTC)


# convert to POSX
GAR_raw$Lokalzeit <- ymd_hms(GAR_raw$Lokalzeit)
GAR_raw$UTC <- ymd_hms(GAR_raw$UTC)
class(GAR_raw$UTC)
[1] "POSIXct" "POSIXt" 
Wdat_GAR <- GAR_raw %>%
  filter(., between(Lokalzeit, start_time, end_time))
# this took way too long...




# Freiburg Stadtklimastation
URB_raw <- read.csv("produkt_air_temperature_13667_akt.txt", sep=";")
# convert POSX
URB_raw$MESS_DATUM <- ymd_h(URB_raw$MESS_DATUM)
# and clip
Wdat_URB <- URB_raw %>% 
  filter(.,between(MESS_DATUM, start_time, end_time)) %>% 
  select(MESS_DATUM, LUFTTEMPERATUR) %>% 
  mutate(dttm=dttm)
# phew..correct nr of observations this time..can deselect orig. timestamp later 
# TODO


# DWD Station 1443
DWD_raw <- read.csv("produkt_tu_stunde_20210718_20230118_01443.txt", sep=";")
# POSX conversio
DWD_raw$MESS_DATUM <- ymd_h(DWD_raw$MESS_DATUM)
Wdat_DWD <- DWD_raw %>% 
  filter(., between(MESS_DATUM, start_time, end_time)) %>% 
  select(., MESS_DATUM, TT_TU) %>% 
  mutate(dttm=dttm)


# check datetimes
# 
# test <- Wdat_DWD %>% 
#   select(MESS_DATUM, dttm) %>% 
#   mutate(Gartime=Wdat_GAR$Lokalzeit) %>% 
#   mutate(Urbtime=Wdat_URB$MESS_DATUM) %>% 
#   mutate(wbitime=Wdat_WBI)
# jawoll this worked...

# stations df
tempDF <- Wdat_DWD %>% 
  select(., time=dttm, tempDWD=TT_TU) %>% 
  mutate(tempURB=Wdat_URB$LUFTTEMPERATUR) %>% 
  mutate(tempGar=Wdat_GAR$Lufttemperatur...C.) %>% 
  mutate(tempWBI=Wdat_WBI$temp)

# tempWBI still has the comma decimals
tempDF$tempWBI <- scan(text=tempDF$tempWBI, dec=",", sep=".")
Read 912 items
# my hobo df
# getwd()
myHOBO <- read.csv("10350049_Th.csv" )

tempDF <- tempDF %>% 
  mutate(myHOBO$th)

# # tempDF <- as_tibble(tempDF)
# tempDF$tempWBI <- as.numeric(tempDF$tempWBI)

tempDF <- cbind(tempDF,HOBOtemp=myHOBO$th)
tempDF$HOBOtemp <- as.numeric(tempDF$HOBOtemp)
Warning: NAs introduced by coercion
# manualy zoomed in comparison graph

date1 <- tempDF$time[210]
date2 <- tempDF$time[245]

ggplot(tempDF,aes(time))+
  geom_line(tempDF,mapping=aes(y=tempURB),color="blue")+
  geom_line(tempDF,mapping=aes(y=tempGar),color="black")+
  geom_line(tempDF,mapping=aes(y=tempWBI),color="green")+
  geom_line(tempDF,mapping=aes(y=tempDWD),color="red")+
  geom_line(tempDF,mapping=aes(y=HOBOtemp),color="purple")+
  ylim(-5,5)+
  xlim(c(date1,date2))


# tempWBI is the closest fit

ggplot(tempDF,aes(time))+
  #geom_line(tempDF,mapping=aes(y=tempURB),color="blue")+
  #geom_line(tempDF,mapping=aes(y=tempGar),color="black")+
  geom_line(tempDF,mapping=aes(y=tempWBI),color="green")+
  #geom_line(tempDF,mapping=aes(y=tempDWD),color="red")+
  geom_line(tempDF,mapping=aes(y=HOBOtemp),color="purple")

  #ylim(-5,5)+
  #xlim(c(date1,date2))

# TODO plot legends and axis labels, color palette

# MODEL 1: WBI
modWBI <- lm(tempDF$HOBOtemp~tempDF$tempWBI, tempDF)
summary(modWBI)

Call:
lm(formula = tempDF$HOBOtemp ~ tempDF$tempWBI, data = tempDF)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.9860 -0.4251 -0.0575  0.3632  3.9620 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    0.384386   0.037086   10.37   <2e-16 ***
tempDF$tempWBI 0.910673   0.004279  212.83   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.8583 on 860 degrees of freedom
  (50 observations deleted due to missingness)
Multiple R-squared:  0.9814,    Adjusted R-squared:  0.9813 
F-statistic: 4.53e+04 on 1 and 860 DF,  p-value: < 2.2e-16
# MODEL 2: DWD
modDWD <- lm(tempDF$HOBOtemp~tempDF$tempDWD, tempDF)
summary(modDWD)

Call:
lm(formula = tempDF$HOBOtemp ~ tempDF$tempDWD, data = tempDF)

Residuals:
    Min      1Q  Median      3Q     Max 
-13.734  -5.084  -0.130   5.535  12.591 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)     4.9510105  0.2121931  23.333  < 2e-16 ***
tempDF$tempDWD -0.0069749  0.0009853  -7.079 3.02e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.113 on 860 degrees of freedom
  (50 observations deleted due to missingness)
Multiple R-squared:  0.05506,   Adjusted R-squared:  0.05396 
F-statistic: 50.11 on 1 and 860 DF,  p-value: 3.021e-12
# MODEL 3: URB
modURB <- lm(tempDF$HOBOtemp~tempDF$tempURB, tempDF)
summary(modURB)

Call:
lm(formula = tempDF$HOBOtemp ~ tempDF$tempURB, data = tempDF)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.5554 -0.4248  0.0194  0.3621  3.9142 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -0.340480   0.037668  -9.039   <2e-16 ***
tempDF$tempURB  0.943340   0.004249 221.999   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.8235 on 860 degrees of freedom
  (50 observations deleted due to missingness)
Multiple R-squared:  0.9828,    Adjusted R-squared:  0.9828 
F-statistic: 4.928e+04 on 1 and 860 DF,  p-value: < 2.2e-16
# MODEL 4: GAR
modGAR <- lm(tempDF$HOBOtemp~tempDF$tempGar, tempDF)
summary(modGAR)

Call:
lm(formula = tempDF$HOBOtemp ~ tempDF$tempGar, data = tempDF)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.4704 -0.4985 -0.0718  0.3866  6.0194 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -0.164629   0.051511  -3.196  0.00144 ** 
tempDF$tempGar  0.933225   0.005853 159.446  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.137 on 859 degrees of freedom
  (51 observations deleted due to missingness)
Multiple R-squared:  0.9673,    Adjusted R-squared:  0.9673 
F-statistic: 2.542e+04 on 1 and 859 DF,  p-value: < 2.2e-16
# table of model coefficients & R^2

Intercepts <- c(summary(modDWD)$coefficients[1,4],
                  summary(modURB)$coefficients[1,4],
                  summary(modGAR)$coefficients[1,4],
                  summary(modWBI)$coefficients[1,4])

R_squ <- c(summary(modDWD)$r.squared,
           summary(modURB)$r.squared,
           summary(modGAR)$r.squared,
           summary(modWBI)$r.squared)

stations <- c("DWD","URB","GAR","WBI")

MOD_RES <- arrange(tibble(stations,Intercepts,R_squ))

summary(modWBI)

Call:
lm(formula = tempDF$HOBOtemp ~ tempDF$tempWBI, data = tempDF)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.9860 -0.4251 -0.0575  0.3632  3.9620 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    0.384386   0.037086   10.37   <2e-16 ***
tempDF$tempWBI 0.910673   0.004279  212.83   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.8583 on 860 degrees of freedom
  (50 observations deleted due to missingness)
Multiple R-squared:  0.9814,    Adjusted R-squared:  0.9813 
F-statistic: 4.53e+04 on 1 and 860 DF,  p-value: < 2.2e-16
# the WBI model sports the best fit according to R^2
# corroborates what we saw in the plot

4.2. fill-up


# fill in NA using regression coefficients from the WBI model

predDF <- tibble(HOBOtemp=tempDF$HOBOtemp,tempWBI=tempDF$tempWBI)

preds <- predict(modWBI, tempDF,type="response")

comparison <- tibble(HOBOorig=tempDF$HOBOtemp,
                     tempWBI=tempDF$tempWBI,
                     predicted=preds)

head(comparison)

# make corrected HOBO vector

# create result df

# fill in NAs with model predictions

tempDF <- tempDF %>% 
  mutate(HOBOcorr=case_when(is.na(HOBOtemp)~preds,is.numeric(HOBOtemp)~HOBOtemp))


hobo_hr_corr <- tempDF %>%
  select(dttm=time, th=HOBOcorr) %>%
  mutate(origin="H")


hobo_hr_corr$origin[which(is.na(tempDF$HOBOtemp))] <- "R"


hobo_hr_corr$dttm <- format(hobo_hr_corr$dttm, "%Y-%m-%d %H:%M:%S")
hobo_hr_corr$th <-  format(hobo_hr_corr$th, digits=3, nsmall=3)

write_csv(hobo_hr_corr, file = "10350049_Th.csv" )

5. Calculate indices

5.1. Generate metadata overview

Generate a human readable overview of all HOBOs for this term.
qc2 <- rollapply(HOBO_wip$delta, width=6,FUN=flagP) # width parameter is window

5.2 Calculate Indices

Calculate temperature indices for only one timeseries. You can pick any HOBO you like. Needed indices: * Calculate the mean temperature * Calculate the mean night temperature * Calculate the daytime coefficient of variation

5.3 Create indices table/view/query

Calculate temperature indices for all HOBOs of this term: The indices necessary are:

  • Mean temperature
  • Mean daytime temperature
  • Mean night temperature
  • Coefficient of variation
  • Length of the timeseries
  • IQR

5.4 Switch the dataset (max. 150 words)

Now apply the same query used in the last task again, but for the other kind of data.

Disscuss differences between the two tables: Are there differences? Which indices are affected most? Why?

5.5 Combine the table with R

In R, calculate either of the following indices and finally merge the two tables: * Mean time lag between maximum light intensity and maximum temperature * Mean of the maximum daily temperature change (per hour)

First, download the data you want to use:

Finally, with the index in place, download the indices overview as created in the last two tasks and combine them in R into one overview table:

6. Spatial analysis

6.1 Find spatial data

You don’t need SQL for this task. Check out the table names and do some research online to find out more about the data used.

The data comes from OpenStreetMap, the largest community driven effort to build a database of global structural geodata, which can be used by everyone under a ODbL license, which includes commercial uses. Geodata standards, like the OSM model but also European standards like INSPIRE are extremely important to make public geodata usable. These datasets are heavily structured and their use often involves many dimensions and large data amounts. Without a standard, each authority would publish different data and a collective use is in fact impossible.

6.2 Filter by location

Filter the database for only the city districts, that contain a reference station and present them in either in a human readable table or a map.

First, create a sub-query/view/with statement containing all reference stations. Then extent this with the needed filter

6.3 Aggregate by location

Query all city districts of Freiburg that contain at least three HOBOs and aggregate the indices calculated in Create indices table/view/query for each of these districts and present them as a table. Repeat the procedure for the HOBO locations of WT21 or WT22 (or both). Are there differences? Describe.

6.4 Creating a map

Use the queries constructed in the last task to create a map of Freiburg, that illustrates differences in one (or more) of the temperature indices between the city districts of Freiburg. You can create this map either in R or in QGis.

Get the data here:

Either include the map or make the visualization here:

cleanup

LS0tDQp0aXRsZTogJ0RhdGEgbWFuYWdlbWVudCBwcm90b2NvbCcNCmF1dGhvcjogIk4qKiBCKioiDQpkYXRlOiAiZGF0ZSBvZiBzdWJtaXNzaW9uIg0Kb3V0cHV0Og0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbjogbm8NCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCiMgbW9yZSBpbnRlcm5hbCBzZXR0aW5ncyBjYW4gZ28gaGVyZQ0KIyBDb25zaWRlciBoZWxwIHBhZ2VzIGxpa2U6DQojIGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2xlc3Nvbi0xLmh0bWwNCiMgaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDMvcm1hcmtkb3duLXJlZmVyZW5jZS5wZGYNCg0KYGBgDQoNCiMjIyBMb2FkZWQgcGFja2FnZXMNCg0KYGBge3IgbGlicmFyaWVzLCByZXN1bHRzPUZ9DQojIExvYWQgcGFja2FnZXMNCnJlcXVpcmUoUlBvc3RncmVTUUwpDQpyZXF1aXJlKGdldFBhc3MpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoem9vKSAjIHRvIGV4dGVuZCB0aGUgYXBwbHkgZmFtaWx5IG9mIGZ1bmN0aW9ucyAocm9sbGluZyB3aW5kb3cpDQpgYGANCg0KIyMjIFByZXByb2Nlc3NpbmcgLyBSIGZ1bmN0aW9ucw0KDQpgYGB7ciBwcmVwcm9jZXNzaW5nLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBJZiBkYXRhIHByZXByb2Nlc3NpbmcgaXMgbmVlZGVkLCBkbyBpdCBoZXJlLg0KIyBmdW5jdGlvbnM6DQpmbGFnUiA8LSBmdW5jdGlvbih4KSB7ICMgZnVuY3Rpb24gdG8gZmxhZyB2YWx1ZXMgaW4gcXVhbGl0eSBjaGVjayAxDQogIGlmZWxzZSh4Pj0xLCAxLDApICAgICMgc2V0cyBmbGFnIHRvIDAgd2hlbiBwYXNzZWQsIHRvIDEgd2hlbiBmYWlsZWQNCn0NCg0KZmxhZ1AgPC0gZnVuY3Rpb24oeCl7ICAjIGZ1bmN0aW9uIGZsYWdzIHZhbHVlcyBpbiBxdWFsaXR5IGNoZWNrIDMgKHZhcmlhYmlsaXR5KQ0KICBpZmVsc2UobGVuZ3RoKHVuaXF1ZSh4KSkgPT0gMSwgMSwwKSAgIyBubyB2YXJpYWJpbGl0eSAtPiAxIHVuaXF1ZSB2YWwNCn0gICAgICAgICAgICAgICAgICAgICAgIyBtdXN0IGJlIGFwcGxpZWQgdXNpbmcgYSByb2xsaW5nIHdpbmRvdyANCg0KYGBgDQoNCiMgMS4gSG9ibyBtZXRhIGluZm9ybWF0aW9ucw0KDQpIb2JvIHBpY3R1cmUgYW5kIGRlc2NyaXB0aW9uDQoNCiFbaW1hZ2Ugb2YgbXkgSG9ib10oLi8xMDM1MDA0OS5qcGcpe3dpZHRoPTI1MHB4fQ0KDQoNCiMgMi4gQ29uc2lzdGVudCBIT0JPIGRhdGEgZmlsZQ0KUmVhZCBpbiBmcm9tIEdpdEh1Yg0KYGBge3IgcmF3X2RhdGEsIGV2YWw9VH0NCiMgTG9hZCBkYXRhIGZyb20gR2l0aHViICh0aGVuIGV2YWwgPSBUUlVFKQ0KbXlfSE9CTyA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2RhdGEtaHlkZW52L2RhdGEvbWFzdGVyL2hvYm8vMjAyMy9yYXcvMTAzNTAwNDkuY3N2Iiwgc2tpcCA9IDEpDQojIExvYWQgY2FsaWJyYXRpb24gZmlsZSBmcm9tIEdpdEh1Yg0KdF9jYWwgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9kYXRhLWh5ZGVudi9kYXRhL21hc3Rlci9ob2JvLzIwMjMvY2FsaWJyYXRpb24uY3N2IikNCmBgYA0KRGF0ZS9UaW1lIGNvbHVtbiBtdXN0IGJlIHBhcnNlZCBwcm9wZXJseToNCmBgYHtyIHBhcnNlIGRhdGV0aW1lfQ0KbXlfSE9CTyREYXR1bS5aZWl0Li5HTVQuMDEuMDAgPC0gbWR5X2htcyhteV9IT0JPJERhdHVtLlplaXQuLkdNVC4wMS4wMCkNCg0KYGBgDQoNClRpZHkgdXAgYW5kIHJlbmFtZSB0aGUgY29sdW1ucw0KYGBge3IgdGlkeV9jb2xzfQ0KbXlfSE9CTyA8LSBhc190aWJibGUobXlfSE9CTykgJT4lIA0KICBzZWxlY3QoLiwgMTo0KSAlPiUgDQogIHJlbmFtZShpZCA9IDEsIGR0dG0gPSAyLCB0ZW1wID0gMywgbHV4ID0gNCkNCg0KaGVhZChteV9IT0JPKQ0KYGBgDQoNCg0KIyMgMi4xLiBDYWxpYnJhdGlvbg0KVGhlIGNhbGlicmF0aW9uIGRhdGEgZnJvbSBHaXRIdWIgaXMgYSB0YWJsZSBvZiB0aGUgdGltZSB3aGVuIHRoZSBjYWxpYnJhdGlvbiBtZWFzdXJlbWVudCB3YXMgcGVyZm9ybWVkIGFuZCB0aGUgdGFyZ2V0IHZhbHVlIHRlbXBlcmF0dXJlLiANCg0KYGBge3IgY2FsaWJyYXRpb259DQpjYWx0aW1lIDwtIHRfY2FsJFRoaXMuaXMudGhlLmNhbGlicmF0aW9uLnZhbHVlWzNdICMgdGltZSBvZiBjYWxpYnJhdGlvbg0KDQpjYWxpYl9saW5lIDwtIHdoaWNoKG15X0hPQk8kZHR0bSA9PSBjYWx0aW1lKSAjIGFjY29yZGluZyBsaW5lIGluIHJhdyBkYXRhDQoNCm1lYXNfdGVtcCA8LSBudW0obXlfSE9CT1tjYWxpYl9saW5lLF0kdGVtcCwgZGlnaXRzID0gMykgIyB3aGF0IHRoZSBIT0JPIG1lYXN1cmVkDQoNCm1lYXNfdGVtcCA8LSBtZWFzX3RlbXBbMV0gIyBhcyBub24tbnVtYmVyZWQgbnVtYmVyDQoNCnRydV90ZW1wIDwtIG51bShhcy5udW1lcmljKHRfY2FsJHRvLmNhbGlicmF0ZS55b3VyLkhvYm8ubWVhc3VyZW1lbnRzLmluLi5DLlszXSksDQogICAgICAgICAgICAgICAgZGlnaXRzID0gMykgIyB3aGF0IHRoZSB0YXJnZXQgdmFsdWUgaXMNCg0KdHJ1X3RlbXAgPC0gdHJ1X3RlbXBbMV0gIyB0YXJnZXQgdmFsdWUgYXMgbm9uLW51bWJlcmVkIG51bWJlcg0KDQpjYWxfb2Zmc2V0IDwtIG1lYXNfdGVtcC10cnVfdGVtcCAjIGRpZmZlcmVuY2Ugb2YgdGhlIHR3bw0KDQpjYWxfb2Zmc2V0IDwtIGFzLm51bWVyaWMoY2FsX29mZnNldFsxXSkNCg0KIyBzbyAtMC4yNjcgaXMgbm93IG15IGNhbGlicmF0aW9uIG9mZnNldA0KDQpteV9IT0JPJHRlbXAgPC0gbXlfSE9CTyR0ZW1wLWNhbF9vZmZzZXQgIyBzdWJ0cmFjdCBvZmZzZXQgZnJvbSByYXcgdGVtcGVyYXR1cmUNCmBgYA0KDQoNCiMjIDIuMi4gY3JlYXRlIEhPQk8gZGF0YSBmaWxlDQpUcnVuY2F0ZSBkYXRhIHRvIHRoZSBnaXZlbiB0aW1lIGZyYW1lLiBGaXJzdCBkZWNsYXJlIHRoZSB0YXJnZXQgc3RhcnQgYW5kIGVuZA0KdGltZSB1c2luZyDCtGx1YnJpZGF0ZTo6eW1kX2htc8K0Lg0KYGBge3IgdHJ1bmNhdGUgcmF3IGRhdGF9DQpzdGFydF90aW1lIDwtIHltZF9obXMoIjIwMjItMTItMDEgMDA6MDA6MDAiKSAjIHN0YXJ0IHBvaW50DQplbmRfdGltZSA8LSB5bWRfaG1zKCIyMDIzLTAxLTA3IDIzOjUwOjAwIikgIyBlbmQgcG9pbnQNCnRpbWVfcmFuZ2UgPC0gaW50ZXJ2YWwoc3RhcnRfdGltZSwgZW5kX3RpbWUpICMgbWF5IGJlIHVzZWZ1bCBsYXRlcg0KYGBgDQpGaWx0ZXIgdGhlIHJhdyBkYXRhIHRvIGNsaXAgaXQgYWNjb3JkaW5nIHRvIHRoZSBzdGFydCBhbmQgZW5kIHRpbWVzIGFuZCBhbHNvDQpvdmVyd3JpdGUgdGhlIGlkIGNvbHVtbiB3aXRoIGEgbmV3IHZhbHVlIHN0YXJ0aW5nIGZyb20gYDFgLiBgZm9ybWF0KClgIHdhcyB1c2VkDQp0byBmb3JtYXQgZGVjaW1hbHMgaW4gdGhlIGxpZ2h0IGludGVuc2l0eSBhbmQgdGVtcGVyYXR1cmUgdmVjdG9ycyBhbmQgdG8gY2hhbmdlDQp0aGUgdGltZXN0YW1wIGZvcm1hdC4NCmBgYHtyIGZvcm1hdCBob2JvIGZpbGV9DQpteV9IT0JPIDwtIG15X0hPQk8gJT4lIA0KICBmaWx0ZXIoYmV0d2VlbihkdHRtLCBzdGFydF90aW1lLCBlbmRfdGltZSkpICMgZmlsdGVyIA0KbXlfSE9CTyA8LSBteV9IT0JPICU+JSANCiAgbXV0YXRlKC4sIGlkID0gYygxOmxlbmd0aChteV9IT0JPJGlkKSkpICMgYW5kIGZpeCBJRCBjb2wNCg0KbXlfSE9CTyR0ZW1wIDwtICBmb3JtYXQobXlfSE9CTyR0ZW1wLCBkaWdpdHM9MywgbnNtYWxsPTMpICMgZml4IGRlY2ltYWxzIGluIHRlbXANCm15X0hPQk8kbHV4IDwtICBmb3JtYXQobXlfSE9CTyRsdXgsIGRpZ2l0cz0zLCBuc21hbGw9MykgIyBhbmQgaW4gbGlnaHQgaW50ZW5zaXR5DQoNCm15X0hPQk8kZHR0bSA8LSBmb3JtYXQobXlfSE9CTyRkdHRtLCAiJVktJW0tJWQgJUg6JU0iKSAjIHRpbWVzdGFtcCBmb3JtYXQNCmBgYA0KVGhlIGB0aWJibGU6IG15X0hPQk9gIHdhcyB0aGVuIHdyaXR0ZW4gdG8gZGlzayBhbmQgdXBsb2FkZWQgdG8gR2l0SHViLg0KYGBge3Igd3JpdGUgaG9ibyBmaWxlfQ0KIyB3cml0ZSB0byBmaWxlDQp3cml0ZV9jc3YobXlfSE9CTywgZmlsZSA9ICIxMDM1MDA0OS5jc3YiKQ0KYGBgDQoNCiMjIDIuMy4gdmVyaWZ5IHlvdXIgZmlsZQ0KDQojIDMuIFF1YWxpdHkgY29udHJvbA0KUmVhZCB0aGUgZGF0YSBiYWNrIGluIGZyb20gR2l0SHViLi4uDQpgYGB7ciBmb3JtYXRlZF9kYXRhfQ0KIyBMb2FkIGRhdGEgZnJvbSBHaXRodWIgKHRoZW4gZXZhbCA9IFRSVUUpDQpIT0JPX3FjIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZGF0YS1oeWRlbnYvZGF0YS9tYXN0ZXIvaG9iby8yMDIzLzEwX21pbnV0ZXMvMTAzNTAwNDkuY3N2IikNCmhlYWQoZGF0YSkNCg0KYGBgDQojIyAzLjEuIFF1YWxpdHkgY29udHJvbCBwcm9jZWR1cmVzIChRQ1BzKQ0KDQojIyMgMy4xLjEuIE1lYXN1cmVtZW50IHJhbmdlIChQbGF1c2libGUgdmFsdWVzKQ0KLi4uYW5kIGNoZWNrIGJvdGggdmFsdWUgcmFuZ2VzLg0KYGBge3IgcWNwMSwgZXZhbD1UfQ0KIyBUT0RPIE5Bcw0KcmFuZ2UoSE9CT19xYyR0ZW1wKQ0KcmFuZ2UoSE9CT19xYyRsdXgpDQpgYGANCg0KKipRdWVzdGlvbioqOiBIb3cgbWFueSBkYXRhIHBvaW50cyBhcmUgb3V0c2lkZSB0aGUgbWVhc3VyZW1lbnQgcmFuZ2U/DQoNCioqQW5zd2VyKio6IE5vbmUNCg0KIyMjIDMuMS4yLiBQbGF1c2libGUgcmF0ZSBvZiBjaGFuZ2UNCg0KYGBge3IgcWNwMn0NCiMgbGFnIG9uZSBjb2x1bW4sIHRoZW4gbXV0YXRlIGRpZmZlcmVuY2UgdG8gbmV3IGNvbHVtbg0KSE9CT193aXAgPC0gSE9CT19xYyAlPiUgDQogIG11dGF0ZSguLCBsYWdnZWQ9bGFnKHRlbXApKSAlPiUgDQogIG11dGF0ZSguLCBkZWx0YT10ZW1wLWxhZ2dlZCkNCg0KcmFuZ2UobmEub21pdChIT0JPX3dpcCRkZWx0YSkpICMgZGVsdGEgdmFsdWVzIG91dCBvZiBsZWdhbCByYW5nZT8NCmBgYA0KDQoqKlF1ZXN0aW9uKio6IERlc2NyaWJlIHNob3J0bHkgaG93IG1hbnkgZGF0YSBwb2ludHMgZmFpbGVkIGR1cmluZyB0aGlzIFFDUCBhbmQgZGlzY3VzcyB3aGV0aGVyIHRoZXJlIGlzIGEgY2VydGFpbiBkYXl0aW1lIHBhdHRlcm4gb2YgZmFpbHVyZSBvciBub3Q/DQoNCioqQW5zd2VyKio6DQoNCmBgYHtyIGZsYWcgcWNwMn0NCnFjX1IgPC0gc2FwcGx5KEhPQk9fd2lwJGRlbHRhLGZsYWdSKSAjIGFwcGx5IGZsYWdnaW5nIGZ1bmN0aW9uIA0KDQpIT0JPX3dpcCA8LSBjYmluZChIT0JPX3dpcCxxY19SKSAjIGNvbGxlY3QgcmVzdWx0cyBpbiBuZXcgZGF0YSBmcmFtZQ0KDQpsZW5ndGgod2hpY2gocWNfUj09MSkpICMgY291bnQgb2NjdXJlbmNlIG9mIGJhZCBkYXRhIHBvaW50cw0KIyBUT0RPIGRheS9uaWdodCBwYXR0ZXJuPw0KYGBgDQoNCiMjIyAzLjEuMy4gTWluaW11bSB2YXJpYWJpbGl0eSAoUGVyc2lzdGVuY2UpDQpVc2UgYHpvbzo6cm9sbGFwcGx5KClgIHRvIHJ1biB0aGUgcGVyc2lzdGVuY2UgY2hlY2sgb3ZlciBhIG1vdmluZyB0aW1lIHdpbmRvdyBvZiANCjYgdGltZSBzdGVwcyAoaS5lLiA2MCBtaW51dGVzKS4NCmBgYHtyLCBmbGFnIHFjcDN9DQpxYzIgPC0gcm9sbGFwcGx5KEhPQk9fd2lwJGRlbHRhLCB3aWR0aD02LEZVTj1mbGFnUCkgIyB3aWR0aCBwYXJhbWV0ZXIgaXMgd2luZG93DQpgYGANCg0KKipUYXNrKio6IENvZGUgaW4gdGhpcyBzZWN0aW9uIHNob3VsZCBhbmFseXNlIHRoZSBwZXJzaXN0ZW5jZS4NCg0KYGBge3IgcGVyc2lzdGVuY2V9DQojIFRPRE8NCmBgYA0KDQpDb21waWxlIHJlc3VsdHMNCg0KYGBge3IgcWMzIHJlc3VsdHN9DQpxY19QIDwtIGMoMTpsZW5ndGgoSE9CT193aXAkZGVsdGEpKSAjIHZlY3RvciB0byBzdG9yZSBwZXJzaXN0ZW5jZSBmbGFnDQpxY19QWzE6NV0gPC0gTkEgIyBtYW51YWxseSBmaWxsIHBvc2l0aW9ucyAxIHRocm91Z2ggNQ0KcWNfUFs2Omxlbmd0aChxY19QKV0gPC0gcWMyICMgYXBwZW5kIHJlc3VsdHMNCkhPQk9fd2lwIDwtIGNiaW5kKEhPQk9fd2lwLHFjX1ApICMgY29sbGVjdCBpbiB0aWJibGUNCmBgYA0KDQoNCiMjIyAzLjEuNC4gTGlnaHQgaW50ZW5zaXR5DQpUaGUgSE9CTyB3YXMgc2l0dWF0ZWQgaW4gYSBwbGFudGVyIGF0IGFsbCB0aW1lcyBzbyB0aGVyZSB3YXMgbm8gZGlzdHVyYmFuY2Ugb2YNCnRoZSBsaWdodCBtZWFzdXJlbWVudC4NCmBgYHtyIHFjcDR9DQpoaXN0KEhPQk9fd2lwJGx1eCwgYnJlYWtzID0gMjUpDQpzdW1tYXJ5KEhPQk9fd2lwJGx1eCkNCnNkKEhPQk9fd2lwJGx1eCkNCnRvcGx1eCA8LSBmaWx0ZXIoSE9CT193aXAsbHV4PD01MDAgJiBsdXg+MSkNCmhpc3QodG9wbHV4JGx1eCxicmVha3M9NTApDQpgYGANCg0KQXNzaWduIFNJQ3MgZnJvbSBTT1VSQ0UhISEgdG8gbGlnaHQgbWVhc3VyZW1lbnQgZGF0YSBwb2ludHMNCmBgYHtyIGFzc2lnbiBTSUN9DQpIT0JPX3dpcCA8LSBIT0JPX3dpcCAlPiUgDQogIG11dGF0ZShTSUMgPSBjYXNlX3doZW4obHV4IDw9IDEwIH4gJ05pZ2h0XzAnLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGx1eCA8PSA1MDAgfiAnUmlzZV9TZXRfMScsDQogICAgICAgICAgICAgICAgICAgICAgICAgbHV4IDw9IDIwMDAgfiAnT3ZlcmNhc3RfZnVsbF8yJywNCiAgICAgICAgICAgICAgICAgICAgICAgICBsdXggPD0gMTUwMDAgfiAnT3ZlcmNhc3RfbGlnaHRfMycsDQogICAgICAgICAgICAgICAgICAgICAgICAgbHV4IDw9IDIwMDAwIH4gJ0NsZWFyXzQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGx1eCA8IDUwMDAwIH4gJ1N1bnNoaW5lXzUnLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGx1eCA+PSA1MDAwMCB+ICdCcmlnaHRzaGluZV82JywNCiAgICAgICAgICAgICAgICAgICAgICAgICApKQ0KDQp1bmlxdWUoSE9CT193aXAkU0lDKSAjIG9ubHkgdGhlIGZpcnN0IGZvdXIgU0lDcyBwcmVzZW50DQpgYGANCioqVGFzayoqOiBEaXNjdXNzIHNob3J0bHkgaG93IG9mdGVuIGFuZCB3aGVuIGR1cmluZyBkYXl0aW1lIHRoZSBRQ1A0IGZsYWdzIGJhZCBkYXRhLiBFbGFib3JhdGUgb24gc29tZSByZWFzb25zIGZvciB5b3VyIHJlc3VsdHMuDQoNCioqQW5zd2VyKio6DQpgYGB7ciBmbGFnIHN1bW1hcnl9DQojIGFuYWx5c2Ugb2NjdXJlbmNlIG9mIGZsYWdzIGFuZCBhZ2dyZWdhdGUgcWMgZmFpbHMgDQpIT0JPX3dpcCA8LSBIT0JPX3dpcCAlPiUgDQogIG11dGF0ZShxY190b3QgPSBxY19QICsgcWNfUikNCg0KSE9CT193aXAkcWNfdG90WzE6NV0gPC0gMA0KDQoNCkhPQk9fd2lwIDwtIEhPQk9fd2lwICU+JSANCiAgbXV0YXRlKHFjX2FsbCA9IGNhc2Vfd2hlbihxY190b3QgPT0gMCB+IDAsDQogICAgICAgICAgICAgICAgICAgcWNfdG90ICE9IDAgfiAxKSkNCg0KIyBhcyBwZXJjZW50YWdlDQoNCnFjX3Jlc3VsdCA8LSB0YWJsZShIT0JPX3dpcCRxY19hbGwpDQoNCnFjX3Jlc3VsdA0KDQoNCm51bSgoKHFjX3Jlc3VsdFsyXS9sZW5ndGgoSE9CT193aXAkcWNfYWxsKSkqMTAwKSxkaWdpdHM9NCkNCiMgVE9ETw0KIyBQcmVzZW50IGEgdGFibGUgb3IgZ3JhcGggdG8gc2hvdyBob3cgbWFueSBkYXRhIHBvaW50cyBmYWlsIGR1cmluZyB0aGUgZm91ciBzcGVjaWZpYyBRQ1BzLiBEaXNjdXNzIHNob3J0bHkNCiMgdGhlIHJlYXNvbnMgZm9yIGZhaWx1cmUgYW5kIGNvbXBhcmUgdGhlIGRpZmZlcmVudCBRQ1BzIGFnYWluc3QgZWFjaCBvdGhlci4NCiMgY3JlYXRlIHFjX2RmDQojIFRPRE8gDQpxY19kZiA8LSBhcy50aWJibGUoSE9CT193aXApDQoNCiMgbnVtYmVyaW5nIGFsbCBob3Vycw0KaG91cl9jdCA8LSByZXAoYygxOjkxMiksIGVhY2ggPSA2KQ0KIyBzdGljayBpdCB0byB0aGUgZGYNCnFjX2RmIDwtIGNiaW5kKHFjX2RmLCBoX2N0ID0gaG91cl9jdCkNCg0KIyBxY19kZiA8LSBxY19kZiAlPiUgDQojICAgbXV0YXRlKGZsYWdfKQ0KDQp0YWJsZShxY19kZiRxY190b3QpDQojIEkgaGF2ZSAyMDMgY2FzZXMgYnV0IGVhY2ggb25lIGhhcyBmYWlsZWQgb25seSBvbmUgb2YgdGhlIHF1YWxpdHkgY2hlY2tzDQoNCnRlc3QgPC0gcWNfZGYgJT4lIA0KICBncm91cF9ieSguLCBoX2ZsYWcgPSBoX2N0KSAlPiUgDQogIHN1bW1hcml6ZSguLCBzdW1fZmxhZ3M9c3VtKHFjX2FsbCkpICU+JSANCiAgZmlsdGVyKHN1bV9mbGFncz49MikNCiMgdGhpcyB5aWVsZHMgYSB2ZWN0b3Igb2YgYWxsIGhvdXJzIHRoYXQgbXVzdCBiZSBzZXQgdG8gTkENCmJhZF9ob3VycyA8LSB0ZXN0JGhfZmxhZw0KDQpxY19kZjIgPC0gcWNfZGYNCg0KYmFkX3RlbXBzIDwtIHdoaWNoKHFjX2RmMiRoX2N0ICVpbiUgYmFkX2hvdXJzKQ0KcWNfZGYyJHRlbXBbYmFkX3RlbXBzXSA8LSBOQQ0KYGBgDQpTaGFyZSBvZiBOQXMNCmBgYHtyIE5BIHNoYXJlfQ0KIyBDYWxjdWxhdGUgdGhlIHNoYXJlIG9mIE5BcyBpbiB5b3VyIGhvdXJseSB0aW1lLXNlcmllcyBpbiAlIGFuZCB3cml0ZSBpdCBpbnRvIHRoZQ0KIyBtZXRhIHRhYmxlIChzaGVldDog4oCcUXVhbGl0eQ0KDQoNCmhybHlfZGF0IDwtIHFjX2RmMiAlPiUgDQogIGdyb3VwX2J5KC4sIGhfY3QpICU+JSANCiAgc3VtbWFyaXNlKC4sIG1lYW5fdGVtcD1tZWFuKHRlbXApKQ0KIyB0aGlzIHlpZWxkcyBob3VybHkgbWVhbnMgd2l0aCBOQXMgDQojIHNoYXJlIG9mIE5BIGluIGhvdXJseSB0aW1lIHNlcmllcw0KDQp0YWJsZShpcy5uYShocmx5X2RhdCRtZWFuX3RlbXApKQ0KIyA1MCBOQXMNCg0KbnVtKCg1MC9sZW5ndGgoaHJseV9kYXQkbWVhbl90ZW1wKSkqMTAwLGRpZ2l0cyA9IDQpDQoNCmBgYA0KDQoNCiMjIDMuMi4gU3VtbWFyaXplDQoNCmBgYHtyIHN1bW1hcml6ZX0NCiMgY29kZSBmb3Igc3VtbWFyaXppbmcgaGVyZQ0KIyBUT0RPDQpgYGANCg0KKipUYXNrKio6IFByZXNlbnQgYSB0YWJsZSBvciBncmFwaCB0byBzaG93IGhvdyBtYW55IGRhdGEgcG9pbnRzIGZhaWwgZHVyaW5nIHRoZSBmb3VyIHNwZWNpZmljIFFDUHMuIERpc2N1c3Mgc2hvcnRseSB0aGUgcmVhc29ucyBmb3IgZmFpbHVyZSBhbmQgY29tcGFyZSB0aGUgZGlmZmVyZW50IFFDUHMgYWdhaW5zdCBlYWNoIG90aGVyLg0KDQoqKkFuc3dlcioqOg0KDQoqKlRhc2sqKjogQXQgdGhlIGVuZCBvZiB0aGUgY29kZSBzZWN0aW9uIGFib3ZlIHlvdSBzaG91bGQgZ2VuZXJhdGUgb25lISB0aWJibGUgb3IgZGF0YS5mcmFtZSBuYW1lZCBgcWNfZGZgIHdpdGggYWxsIHRpbWUgaW5mb3JtYXRpb24sIGFsbCBkYXRhIHBvaW50cyAodGVtcGVyYXR1cmUgYW5kIGx1eCkgYW5kIHlvdXIgb3V0Y29tZXMgb2YgdGhlIGRpZmZlcmVudCBRQ1BzLg0KDQoqKkFuc3dlcioqOg0KDQojIyAzLjMuIEFnZ3JlZ2F0ZQ0KDQpgYGB7ciBhZ2d9DQojIFRPRE8gc2VwYXJhdGUgYWdncmVnYXRpb24gY2h1bmsNCmBgYA0KDQoNCioqVGFzayoqOiBBdCB0aGUgZW5kIG9mIHRoZSBjb2RlIHNlY3Rpb24gYWJvdmUgeW91IHNob3VsZCBnZW5lcmF0ZSBvbmUhIHRpYmJsZSBvciBkYXRhLmZyYW1lIG5hbWVkIGBob2JvX2hvdXJseWAgd2l0aCBhdmVyYWdlZCB0ZW1wZXJhdHVyZSB2YWx1ZXMgcGVyIGhvdXIgb3IgTkEgdmFsdWVzIChpZiB0aGUgaG91ciBpcyBmbGFnZ2VkIGFzIGJhZCBkYXRhKS4gU2VlIGV4ZXJjaXNlIGRlc2NyaXB0aW9uIGZvciBtb3JlIGRldGFpbHMuDQoNCmBgYHtyIHdyaXRlIHFjIGZpbGV9DQpkdHRtIDwtIHNlcShzdGFydF90aW1lLCBlbmRfdGltZSwgYnk9ICJob3VycyIpDQoNCmhvYm9faG91cmx5IDwtIGhybHlfZGF0ICU+JSANCiAgc2VsZWN0KHRoPW1lYW5fdGVtcCkgJT4lIA0KICBtdXRhdGUoZGF0ZV90aW1lPWR0dG0pICU+JSANCiAgbXV0YXRlKG9yaWdpbj1yZXAoIkgiKSkNCg0KaG9ib19ob3VybHkkZGF0ZV90aW1lIDwtIGZvcm1hdChob2JvX2hvdXJseSRkYXRlX3RpbWUsICIlWS0lbS0lZCAlSDolTTolUyIpDQpob2JvX2hvdXJseSR0aCA8LSAgZm9ybWF0KGhvYm9faG91cmx5JHRoLCBkaWdpdHM9MywgbnNtYWxsPTMpDQoNCndyaXRlX2Nzdihob2JvX2hvdXJseSwgZmlsZSA9ICIxMDM1MDA0OV9UaC5jc3YiICkNCg0KYGBgDQoNCg0KIyA0LiBGaWxsLXVwIHdpdGggcmVmZXJlbmNlIHN0YXRpb24NCg0KIyMgNC4xLiByZWZlcmVuY2Ugc3RhdGlvbg0KDQpgYGB7ciByZWYgc3RhdGlvbn0NCg0KIyBmaWxsIHVwIGFsbCB0aGUgTkEgaW4geW91cg0KIyBob2JvX2hvdXJseSBmcm9tIGNoYXB0ZXIgMy4zIGJhc2VkIG9uIGEgcmVncmVzc2lvbiBtb2RlbCBiZXR3ZWVuIHlvdXIgc3RhdGlvbg0KIyBhbmQgYSByZWZlcmVuY2Ugc3RhdGlvbi4gDQoNCiMgV0JJIFN0YXRpb24NCiMgZGF0YSBpbXBvcnQgZnJvbSBoZGQNCldCSV9yYXcgPC0gcmVhZC5jc3YoIlN0dW5kZV8wOTYuY3N2Iiwgc2VwPSI7IikNCiMgY29udmVydCB0byBwcm9wZXIgUE9TWCBvciBsZCBvYmplY3QNCldCSV9yYXckVGFnIDwtIGRteShXQklfcmF3JFRhZykNCmNsYXNzKFdCSV9yYXckVGFnKQ0KIyB3b3JrcyBzZW1pIHdlbGwgd2l0aCB0aGlzIHRpbWVzdGFtcC4uLg0KV0JJX3JhdyRTdHVuZGUgPC0gaG0oV0JJX3JhdyRTdHVuZGUpDQpjbGFzcyhXQklfcmF3JFN0dW5kZSkNCg0KIyBpJ2xsIG1ha2UgbXkgb3duDQojIGRlY2xhcmUgdGltZSBmcmFtZQ0Kc3RhcnRfdGltZSA8LSB5bWRfaG1zKCIyMDIyLTEyLTAxIDAwOjAwOjAwIikNCmVuZF90aW1lIDwtIHltZF9obXMoIjIwMjMtMDEtMDcgMjM6NTA6MDAiKQ0KdGltZV9yYW5nZSA8LSBpbnRlcnZhbChzdGFydF90aW1lLCBlbmRfdGltZSkNCiMgbWFrZSBteSBvd24gZHR0bSBhbmQgc2NyYXAgdGhlIG9uZXMgdGhhdCBjYW1lIHdpdGggdGhlIFdCSSBkYXRhDQpkdHRtIDwtIHNlcShzdGFydF90aW1lLCBlbmRfdGltZSwgYnk9ICJob3VycyIpDQojIGNsaXAgV0JJIGRhdGEsIGFkZCBkdHRtIGFuZCByZW5hbWUgY29scw0KV2RhdF9XQkkgPC0gV0JJX3JhdyAlPiUgDQogIGZpbHRlciguLGJldHdlZW4oVGFnLCBkYXRlKHN0YXJ0X3RpbWUpLCBkYXRlKGVuZF90aW1lKSkpICU+JSANCiAgc2VsZWN0KC4sdGVtcD1BVkdfVEEyMDApICU+JSANCiAgbXV0YXRlKGR0dG09ZHR0bSkNCiMgVE9ETyBmb3JtYXQgdGhlIHRpbWVzdGFtcCB0byBnZXQgcmlkIG9mIHNlY29uZHMgZXZlbnR1YWxseSAoaWYgdGhlIGFzc2lnbm1lbnQgc2F5cyBzbykNCg0KDQojIEZSRUlCVVJHIEdhcnRlbiBzdGF0aW9uDQpHQVJfcmF3IDwtIHJlYWQuY3N2KCJGcmVpYnVyZ19HYXJ0ZW5fMjAyMi0xMS0zMF8yMDIzLTAxLTA4LmNzdiIpDQoNCiMgY29ycmVjdCBtaXNzaW5nIGxpbmUgaW4gR0FSX3Jhdw0KR0FSX3JhdyA8LSBHQVJfcmF3ICU+JSANCiAgYWRkX3JvdyhVVEMgPSAiMjAyMi0xMi0xNiAxMDowMDowMCIsIExva2FsemVpdCA9ICIyMDIyLTEyLTE2IDExOjAwOjAwIiwgTHVmdHRlbXBlcmF0dXIuLi5DLiA9IE5BLCAuYmVmb3JlID0gMzk1KQ0KDQpHQVJfcmF3WzM5MDozOTYsXQ0KIyB0aGF0J3MgYmV0dGVyLCBub3cgdGhlIHdob2xlIGNvbnZlcnRpbmcgYW5kIGNsaXBwaW5nIHByb2NlZHVyZSBhZ2Fpbg0KDQojIEdBUl9yYXckTG9rYWx6ZWl0IDwtIHltZF9obXMoR0FSX3JhdyRMb2thbHplaXQsIHR6PSJFdXJvcGUvQmVybGluIikNCiMgR0FSX3JhdyRVVEMgPC0geW1kX2htcyhHQVJfcmF3JFVUQywgdHo9IlVUQyIpDQojIGNsYXNzKEdBUl9yYXckTG9rYWx6ZWl0KQ0KIyBjbGFzcyhHQVJfcmF3JFVUQykNCg0KDQojIGNvbnZlcnQgdG8gUE9TWA0KR0FSX3JhdyRMb2thbHplaXQgPC0geW1kX2htcyhHQVJfcmF3JExva2FsemVpdCkNCkdBUl9yYXckVVRDIDwtIHltZF9obXMoR0FSX3JhdyRVVEMpDQpjbGFzcyhHQVJfcmF3JFVUQykNCg0KV2RhdF9HQVIgPC0gR0FSX3JhdyAlPiUNCiAgZmlsdGVyKC4sIGJldHdlZW4oTG9rYWx6ZWl0LCBzdGFydF90aW1lLCBlbmRfdGltZSkpDQojIHRoaXMgdG9vayB3YXkgdG9vIGxvbmcuLi4NCg0KDQoNCg0KIyBGcmVpYnVyZyBTdGFkdGtsaW1hc3RhdGlvbg0KVVJCX3JhdyA8LSByZWFkLmNzdigicHJvZHVrdF9haXJfdGVtcGVyYXR1cmVfMTM2NjdfYWt0LnR4dCIsIHNlcCA9ICI7IikNCiMgY29udmVydCBQT1NYDQpVUkJfcmF3JE1FU1NfREFUVU0gPC0geW1kX2goVVJCX3JhdyRNRVNTX0RBVFVNKQ0KIyBhbmQgY2xpcA0KV2RhdF9VUkIgPC0gVVJCX3JhdyAlPiUgDQogIGZpbHRlciguLGJldHdlZW4oTUVTU19EQVRVTSwgc3RhcnRfdGltZSwgZW5kX3RpbWUpKSAlPiUgDQogIHNlbGVjdChNRVNTX0RBVFVNLCBMVUZUVEVNUEVSQVRVUikgJT4lIA0KICBtdXRhdGUoZHR0bSA9IGR0dG0pDQojIHBoZXcuLmNvcnJlY3QgbnIgb2Ygb2JzZXJ2YXRpb25zIHRoaXMgdGltZS4uY2FuIGRlc2VsZWN0IG9yaWcuIHRpbWVzdGFtcCBsYXRlciANCiMgVE9ETw0KDQoNCiMgRFdEIFN0YXRpb24gMTQ0Mw0KRFdEX3JhdyA8LSByZWFkLmNzdigicHJvZHVrdF90dV9zdHVuZGVfMjAyMTA3MThfMjAyMzAxMThfMDE0NDMudHh0Iiwgc2VwID0gIjsiKQ0KIyBQT1NYIGNvbnZlcnNpbw0KRFdEX3JhdyRNRVNTX0RBVFVNIDwtIHltZF9oKERXRF9yYXckTUVTU19EQVRVTSkNCldkYXRfRFdEIDwtIERXRF9yYXcgJT4lIA0KICBmaWx0ZXIoLiwgYmV0d2VlbihNRVNTX0RBVFVNLCBzdGFydF90aW1lLCBlbmRfdGltZSkpICU+JSANCiAgc2VsZWN0KC4sIE1FU1NfREFUVU0sIFRUX1RVKSAlPiUgDQogIG11dGF0ZShkdHRtID0gZHR0bSkNCg0KDQojIGNoZWNrIGRhdGV0aW1lcw0KIyANCiMgdGVzdCA8LSBXZGF0X0RXRCAlPiUgDQojICAgc2VsZWN0KE1FU1NfREFUVU0sIGR0dG0pICU+JSANCiMgICBtdXRhdGUoR2FydGltZT1XZGF0X0dBUiRMb2thbHplaXQpICU+JSANCiMgICBtdXRhdGUoVXJidGltZT1XZGF0X1VSQiRNRVNTX0RBVFVNKSAlPiUgDQojICAgbXV0YXRlKHdiaXRpbWU9V2RhdF9XQkkpDQojIGphd29sbCB0aGlzIHdvcmtlZC4uLg0KDQojIHN0YXRpb25zIGRmDQp0ZW1wREYgPC0gV2RhdF9EV0QgJT4lIA0KICBzZWxlY3QoLiwgdGltZSA9IGR0dG0sIHRlbXBEV0QgPSBUVF9UVSkgJT4lIA0KICBtdXRhdGUodGVtcFVSQiA9IFdkYXRfVVJCJExVRlRURU1QRVJBVFVSKSAlPiUgDQogIG11dGF0ZSh0ZW1wR2FyID0gV2RhdF9HQVIkTHVmdHRlbXBlcmF0dXIuLi5DLikgJT4lIA0KICBtdXRhdGUodGVtcFdCSSA9IFdkYXRfV0JJJHRlbXApDQoNCiMgdGVtcFdCSSBzdGlsbCBoYXMgdGhlIGNvbW1hIGRlY2ltYWxzDQp0ZW1wREYkdGVtcFdCSSA8LSBzY2FuKHRleHQ9dGVtcERGJHRlbXBXQkksIGRlYyA9ICIsIiwgc2VwID0gIi4iKQ0KDQoNCiMgbXkgaG9ibyBkZg0KIyBnZXR3ZCgpDQpteUhPQk8gPC0gcmVhZC5jc3YoIjEwMzUwMDQ5X1RoLmNzdiIgKQ0KDQp0ZW1wREYgPC0gdGVtcERGICU+JSANCiAgbXV0YXRlKG15SE9CTyR0aCkNCg0KIyAjIHRlbXBERiA8LSBhc190aWJibGUodGVtcERGKQ0KIyB0ZW1wREYkdGVtcFdCSSA8LSBhcy5udW1lcmljKHRlbXBERiR0ZW1wV0JJKQ0KDQp0ZW1wREYgPC0gY2JpbmQodGVtcERGLEhPQk90ZW1wID0gbXlIT0JPJHRoKQ0KdGVtcERGJEhPQk90ZW1wIDwtIGFzLm51bWVyaWModGVtcERGJEhPQk90ZW1wKQ0KDQoNCiMgbWFudWFseSB6b29tZWQgaW4gY29tcGFyaXNvbiBncmFwaA0KDQpkYXRlMSA8LSB0ZW1wREYkdGltZVsyMTBdDQpkYXRlMiA8LSB0ZW1wREYkdGltZVsyNDVdDQoNCmdncGxvdCh0ZW1wREYsYWVzKHRpbWUpKSsNCiAgZ2VvbV9saW5lKHRlbXBERixtYXBwaW5nPWFlcyh5PXRlbXBVUkIpLGNvbG9yID0gImJsdWUiKSsNCiAgZ2VvbV9saW5lKHRlbXBERixtYXBwaW5nPWFlcyh5PXRlbXBHYXIpLGNvbG9yID0gImJsYWNrIikrDQogIGdlb21fbGluZSh0ZW1wREYsbWFwcGluZz1hZXMoeT10ZW1wV0JJKSxjb2xvciA9ICJncmVlbiIpKw0KICBnZW9tX2xpbmUodGVtcERGLG1hcHBpbmc9YWVzKHk9dGVtcERXRCksY29sb3IgPSAicmVkIikrDQogIGdlb21fbGluZSh0ZW1wREYsbWFwcGluZz1hZXMoeT1IT0JPdGVtcCksY29sb3IgPSAicHVycGxlIikrDQogIHlsaW0oLTUsNSkrDQogIHhsaW0oYyhkYXRlMSxkYXRlMikpDQoNCiMgdGVtcFdCSSBpcyB0aGUgY2xvc2VzdCBmaXQNCg0KZ2dwbG90KHRlbXBERixhZXModGltZSkpKw0KICAjZ2VvbV9saW5lKHRlbXBERixtYXBwaW5nPWFlcyh5PXRlbXBVUkIpLGNvbG9yPSJibHVlIikrDQogICNnZW9tX2xpbmUodGVtcERGLG1hcHBpbmc9YWVzKHk9dGVtcEdhciksY29sb3I9ImJsYWNrIikrDQogIGdlb21fbGluZSh0ZW1wREYsbWFwcGluZz1hZXMoeT10ZW1wV0JJKSxjb2xvcj0iZ3JlZW4iKSsNCiAgI2dlb21fbGluZSh0ZW1wREYsbWFwcGluZz1hZXMoeT10ZW1wRFdEKSxjb2xvcj0icmVkIikrDQogIGdlb21fbGluZSh0ZW1wREYsbWFwcGluZz1hZXMoeT1IT0JPdGVtcCksY29sb3I9InB1cnBsZSIpDQogICN5bGltKC01LDUpKw0KICAjeGxpbShjKGRhdGUxLGRhdGUyKSkNCg0KIyBUT0RPIHBsb3QgbGVnZW5kcyBhbmQgYXhpcyBsYWJlbHMsIGNvbG9yIHBhbGV0dGUNCg0KIyBNT0RFTCAxOiBXQkkNCm1vZFdCSSA8LSBsbSh0ZW1wREYkSE9CT3RlbXB+dGVtcERGJHRlbXBXQkksIHRlbXBERikNCnN1bW1hcnkobW9kV0JJKQ0KDQojIE1PREVMIDI6IERXRA0KbW9kRFdEIDwtIGxtKHRlbXBERiRIT0JPdGVtcH50ZW1wREYkdGVtcERXRCwgdGVtcERGKQ0Kc3VtbWFyeShtb2REV0QpDQoNCiMgTU9ERUwgMzogVVJCDQptb2RVUkIgPC0gbG0odGVtcERGJEhPQk90ZW1wfnRlbXBERiR0ZW1wVVJCLCB0ZW1wREYpDQpzdW1tYXJ5KG1vZFVSQikNCg0KIyBNT0RFTCA0OiBHQVINCm1vZEdBUiA8LSBsbSh0ZW1wREYkSE9CT3RlbXB+dGVtcERGJHRlbXBHYXIsIHRlbXBERikNCnN1bW1hcnkobW9kR0FSKQ0KDQojIHRhYmxlIG9mIG1vZGVsIGNvZWZmaWNpZW50cyAmIFJeMg0KDQpJbnRlcmNlcHRzIDwtIGMoc3VtbWFyeShtb2REV0QpJGNvZWZmaWNpZW50c1sxLDRdLA0KICAgICAgICAgICAgICAgICAgc3VtbWFyeShtb2RVUkIpJGNvZWZmaWNpZW50c1sxLDRdLA0KICAgICAgICAgICAgICAgICAgc3VtbWFyeShtb2RHQVIpJGNvZWZmaWNpZW50c1sxLDRdLA0KICAgICAgICAgICAgICAgICAgc3VtbWFyeShtb2RXQkkpJGNvZWZmaWNpZW50c1sxLDRdKQ0KDQpSX3NxdSA8LSBjKHN1bW1hcnkobW9kRFdEKSRyLnNxdWFyZWQsDQogICAgICAgICAgIHN1bW1hcnkobW9kVVJCKSRyLnNxdWFyZWQsDQogICAgICAgICAgIHN1bW1hcnkobW9kR0FSKSRyLnNxdWFyZWQsDQogICAgICAgICAgIHN1bW1hcnkobW9kV0JJKSRyLnNxdWFyZWQpDQoNCnN0YXRpb25zIDwtIGMoIkRXRCIsIlVSQiIsIkdBUiIsIldCSSIpDQoNCk1PRF9SRVMgPC0gYXJyYW5nZSh0aWJibGUoc3RhdGlvbnMsSW50ZXJjZXB0cyxSX3NxdSkpDQoNCnN1bW1hcnkobW9kV0JJKQ0KIyB0aGUgV0JJIG1vZGVsIHNwb3J0cyB0aGUgYmVzdCBmaXQgYWNjb3JkaW5nIHRvIFJeMg0KIyBjb3Jyb2JvcmF0ZXMgd2hhdCB3ZSBzYXcgaW4gdGhlIHBsb3QNCg0KDQpgYGANCg0KDQojIyA0LjIuIGZpbGwtdXANCg0KYGBge3IgZmlsbCByZWdyZXNzaW9ufQ0KDQojIGZpbGwgaW4gTkEgdXNpbmcgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgZnJvbSB0aGUgV0JJIG1vZGVsDQoNCnByZWRERiA8LSB0aWJibGUoSE9CT3RlbXA9dGVtcERGJEhPQk90ZW1wLHRlbXBXQkk9dGVtcERGJHRlbXBXQkkpDQoNCnByZWRzIDwtIHByZWRpY3QobW9kV0JJLCB0ZW1wREYsdHlwZT0icmVzcG9uc2UiKQ0KDQpjb21wYXJpc29uIDwtIHRpYmJsZShIT0JPb3JpZz10ZW1wREYkSE9CT3RlbXAsDQogICAgICAgICAgICAgICAgICAgICB0ZW1wV0JJPXRlbXBERiR0ZW1wV0JJLA0KICAgICAgICAgICAgICAgICAgICAgcHJlZGljdGVkPXByZWRzKQ0KDQpoZWFkKGNvbXBhcmlzb24pDQoNCiMgbWFrZSBjb3JyZWN0ZWQgSE9CTyB2ZWN0b3INCg0KIyBjcmVhdGUgcmVzdWx0IGRmDQoNCiMgZmlsbCBpbiBOQXMgd2l0aCBtb2RlbCBwcmVkaWN0aW9ucw0KDQp0ZW1wREYgPC0gdGVtcERGICU+JSANCiAgbXV0YXRlKEhPQk9jb3JyPWNhc2Vfd2hlbihpcy5uYShIT0JPdGVtcCl+cHJlZHMsaXMubnVtZXJpYyhIT0JPdGVtcCl+SE9CT3RlbXApKQ0KDQoNCmhvYm9faHJfY29yciA8LSB0ZW1wREYgJT4lDQogIHNlbGVjdChkdHRtPXRpbWUsIHRoPUhPQk9jb3JyKSAlPiUNCiAgbXV0YXRlKG9yaWdpbj0iSCIpDQoNCg0KaG9ib19ocl9jb3JyJG9yaWdpblt3aGljaChpcy5uYSh0ZW1wREYkSE9CT3RlbXApKV0gPC0gIlIiDQoNCg0KaG9ib19ocl9jb3JyJGR0dG0gPC0gZm9ybWF0KGhvYm9faHJfY29yciRkdHRtLCAiJVktJW0tJWQgJUg6JU06JVMiKQ0KaG9ib19ocl9jb3JyJHRoIDwtICBmb3JtYXQoaG9ib19ocl9jb3JyJHRoLCBkaWdpdHM9MywgbnNtYWxsPTMpDQoNCndyaXRlX2Nzdihob2JvX2hyX2NvcnIsIGZpbGUgPSAiMTAzNTAwNDlfVGguY3N2IiApDQoNCg0KYGBgDQoNCg0KIyA1LglDYWxjdWxhdGUgaW5kaWNlcw0KYGBge3Igc2V0dXBfc3FsLCBpbmNsdWRlPUZBTFNFfQ0KcHcgPC0gZnVuY3Rpb24gKCkgew0KICBpZiAoU3lzLmdldGVudignUE9TVEdSRVNfUEFTU1dPUkQnKSA9PSAiIil7DQogICAgcmV0dXJuKGdldFBhc3MoJ1Byb3ZpZGUgdGhlIHBhc3N3b3JkOiAnKSkNCiAgfSBlbHNlIHsNCiAgICByZXR1cm4oU3lzLmdldGVudignUE9TVEdSRVNfUEFTU1dPUkQnKSkNCiAgfQ0KfQ0KDQojIGVzdGFibGlzaCB0aGUgY29ubmVjdGlvbg0KZHJ2IDwtIGRiRHJpdmVyKCdQb3N0Z3JlU1FMJykNCmNvbiA8LSBkYkNvbm5lY3QoZHJ2LCBob3N0PSdoeWRlbnYuaHlkcm9jb2RlLmRlJywgcG9ydD01NDMyLCB1c2VyPSdoeWRlbnYnLCANCiAgICAgICAgICAgICAgICAgcGFzc3dvcmQ9cHcoKSwgZGJuYW1lPSdoeWRlbnYnKQ0KYGBgDQoNCiMjIDUuMS4JR2VuZXJhdGUgbWV0YWRhdGEgb3ZlcnZpZXcNCg0KPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+R2VuZXJhdGUgYSAqaHVtYW4gcmVhZGFibGUqIG92ZXJ2aWV3IG9mIGFsbCBIT0JPcyBmb3IgdGhpcyB0ZXJtLjwvZGl2Pg0KDQpgYGB7c3FsIGNvbm5lY3Rpb249Y29ufQ0KU0VMRUNUICBtLmlkLCBkZXZpY2VfaWQgYXMgIkhPQk8gaWQiLCBfX18gYXMgbG9uLCBfX18gYXMgbGF0LCBmdWxsX25hbWUgYXMgdGVybSwgZGVzY3JpcHRpb24NCkZST00gbWV0YWRhdGEgbQ0Kam9pbiB0ZXJtcyB0IG9uIF9fXw0Kam9pbiBzZW5zb3JzIHMgb24gX19fDQpXSEVSRSBzLm5hbWU9J2hvYm8nIEFORCBzaG9ydD0nV1QyMicNCmBgYA0KDQojIyA1LjIJQ2FsY3VsYXRlIEluZGljZXMNCg0KPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+Q2FsY3VsYXRlIHRlbXBlcmF0dXJlIGluZGljZXMgZm9yIG9ubHkgb25lIHRpbWVzZXJpZXMuIFlvdSBjYW4gcGljayBhbnkgSE9CTyB5b3UgbGlrZS4gTmVlZGVkIGluZGljZXM6DQoqIENhbGN1bGF0ZSB0aGUgbWVhbiB0ZW1wZXJhdHVyZQ0KKiBDYWxjdWxhdGUgdGhlIG1lYW4gbmlnaHQgdGVtcGVyYXR1cmUNCiogQ2FsY3VsYXRlIHRoZSBkYXl0aW1lIGNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbg0KPC9kaXY+DQpgYGB7c3FsIGNvbm5lY3Rpb249Y29ufQ0Kd2l0aCB0aW1lc2VyaWVzIGFzICgNCiAgc2VsZWN0IHRzdGFtcCwgdmFsdWUgZnJvbSBkYXRhIFdIRVJFIG1ldGFfaWQ9MTUwIGFuZCB2YXJpYWJsZV9pZD0xICAgLS0gcmVwbGFjZSB0aGUgaWQNCiksDQptZWFuX3RlbXAgYXMgKA0KICBzZWxlY3QgNDIgYXMgaWR4IGZyb20gdGltZXNlcmllcyAgIC0tIGNoYW5nZSB0aGUgYWdncmVnYXRpb24NCiksDQptZWFuX25pZ2h0IGFzICgNCiAgc2VsZWN0IDQyIGFzIGlkeCBmcm9tIHRpbWVzZXJpZXMgICAtLSBjaGFuZ2UgdGhlIGFnZ3JlZ2F0aW9uDQogIHdoZXJlIGRhdGVfcGFydCgnaG91cicsIHRzdGFtcCkgPT0gNyANCiksDQpkYXlfY29lZmYgYXMgKA0KICBzZWxlY3QgNDIgYXMgaWR4IGZyb20gdGltZXNlcmllcyAgIC0tIGNoYW5nZSB0aGUgYWdncmVnYXRpb24NCiAgd2hlcmUgZGF0ZV9wYXJ0KCdob3VyJywgdHN0YW1wKSA9PSA3DQopDQpzZWxlY3QgJ21lYW4gdGVtcGVyYXR1cmUnIGFzIGRlc2NyaXB0aW9uLCBpZHggZnJvbSBtZWFuX3RlbXANCnVuaW9uDQpzZWxlY3QgJ25pZ2h0IG1lYW4nIGFzIGRlc2NyaXB0aW9uLCBpZHggZnJvbSBtZWFuX25pZ2h0DQp1bmlvbg0Kc2VsZWN0ICdkYXkgdmFyLiBjb2VmZicgYXMgZGVzY3JpcHRpb24sIGlkeCBmcm9tIGRheV9jb2VmZg0KYGBgDQoNCiMjIDUuMyBDcmVhdGUgaW5kaWNlcyB0YWJsZS92aWV3L3F1ZXJ5DQoNCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPg0KQ2FsY3VsYXRlIHRlbXBlcmF0dXJlIGluZGljZXMgZm9yICoqYWxsKiogSE9CT3Mgb2YgdGhpcyB0ZXJtOiBUaGUgaW5kaWNlcyBuZWNlc3NhcnkgYXJlOg0KDQoqCU1lYW4gdGVtcGVyYXR1cmUNCioJTWVhbiBkYXl0aW1lIHRlbXBlcmF0dXJlDQoqCU1lYW4gbmlnaHQgdGVtcGVyYXR1cmUNCioJQ29lZmZpY2llbnQgb2YgdmFyaWF0aW9uDQoqCUxlbmd0aCBvZiB0aGUgdGltZXNlcmllcw0KKglJUVINCjwvZGl2Pg0KDQpgYGB7c3FsIGNvbm5lY3Rpb249Y29ufQ0Kd2l0aCBjIGFzICgNCiAgc2VsZWN0IG0uaWQsIHRzdGFtcCwgdmFsdWUgZnJvbSBkYXRhIGQNCiAgam9pbiBtZXRhZGF0YSBtIG9uIG0uaWQ9ZC5tZXRhX2lkDQogIGpvaW4gdGVybXMgdCBvbiB0LmlkPW0udGVybV9pZA0KICB3aGVyZQ0KDQoNCiksDQpkYXkgYXMgKA0KDQopLA0KbmlnaHQgYXMgKA0KDQopLA0KbWVhbiBhcyAoDQoNCiksDQpjb2VmZiBhcyAoDQoNCiksDQppZHggYXMgKA0KICBzZWxlY3QgbS5pZCwgbWVhbi5tZWFuLCBkYXkuZGF5X21lYW4sIG5pZ2h0Lm5pZ2h0X21lYW4sIGNvZWZmLnZhcg0KICBmcm9tIG1ldGFkYXRhIG0NCiAgbmF0dXJhbCBqb2luIGRheQ0KICBuYXR1cmFsIGpvaW4gbWVhbg0KICBuYXR1cmFsIGpvaW4gbmlnaHQNCiAgbmF0dXJhbCBqb2luIGNvZWZmDQopDQoNCnNlbGVjdCAqIGZyb20gaWR4DQpgYGANCg0KIyMgNS40CVN3aXRjaCB0aGUgZGF0YXNldCAobWF4LiAxNTAgd29yZHMpDQoNCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPg0KTm93IGFwcGx5IHRoZSBzYW1lIHF1ZXJ5IHVzZWQgaW4gdGhlIGxhc3QgdGFzayBhZ2FpbiwgYnV0IGZvciB0aGUgb3RoZXIga2luZCBvZiBkYXRhLg0KPC9kaXY+DQoNCmBgYHtzcWwgY29ubmVjdGlvbj1jb259DQoNCmBgYA0KDQo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj5EaXNzY3VzcyBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSB0d28gdGFibGVzOiBBcmUgdGhlcmUgZGlmZmVyZW5jZXM/IFdoaWNoIGluZGljZXMgYXJlIGFmZmVjdGVkIG1vc3Q/IFdoeT88L2Rpdj4NCg0KIyMgNS41IENvbWJpbmUgdGhlIHRhYmxlIHdpdGggUg0KDQo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4NCkluIFIsIGNhbGN1bGF0ZSAqKmVpdGhlcioqIG9mIHRoZSBmb2xsb3dpbmcgaW5kaWNlcyBhbmQgZmluYWxseSBtZXJnZSB0aGUgdHdvIHRhYmxlczoNCioJTWVhbiB0aW1lIGxhZyBiZXR3ZWVuIG1heGltdW0gbGlnaHQgaW50ZW5zaXR5IGFuZCBtYXhpbXVtIHRlbXBlcmF0dXJlDQoqCU1lYW4gb2YgdGhlIG1heGltdW0gZGFpbHkgdGVtcGVyYXR1cmUgY2hhbmdlIChwZXIgaG91cikNCjwvZGl2Pg0KDQpGaXJzdCwgZG93bmxvYWQgdGhlIGRhdGEgeW91IHdhbnQgdG8gdXNlOg0KYGBge3NxbCBjb25uZWN0aW9uPWNvbiwgb3V0cHV0LnZhcj0ibXkudGVtcCJ9DQotLSBTZWxlY3Qgb25seSB5b3VyIGhvYm8sIG9yIGRpcmVjdGx5IGFsbCB5b3UgbmVlZA0KU0VMRUNUIHRzdGFtcCwgdmFsdWUgZnJvbSBkYXRhIHdoZXJlIG1ldGFfaWQ9MTUwIGFuZCB2YXJpYWJsZV9pZCA9IDENCmBgYA0KYGBge3J9DQojIE5vdywgYXBwbHkgdGhlIGluZGV4IGNhbGN1bGF0aW9uDQpjYWxjIDwtIGZ1bmN0aW9uKGRmKSB7DQogICMgeW91ciBpbmRleCBjYWxjdWxhdGlvbg0KfQ0KbXkuaWR4IDwtIGNhbGMobXkudGVtcCkNCg0KaGVhZChteS50ZW1wKQ0KYGBgDQoNCkZpbmFsbHksIHdpdGggdGhlIGluZGV4IGluIHBsYWNlLCBkb3dubG9hZCB0aGUgaW5kaWNlcyBvdmVydmlldyBhcyBjcmVhdGVkIGluIHRoZSBsYXN0IHR3byB0YXNrcyBhbmQgY29tYmluZSB0aGVtIGluIFIgaW50byBvbmUgb3ZlcnZpZXcgdGFibGU6DQpgYGB7cn0NCiMgcGxhY2UgeW91ciBjb2RlIGhlcmUgIC0tIHlvdSBjYW4gZGVsZXRlIG15IGNvZGUgaWYgeW91IGRvbid0IHdhbnQgdG8gdXNlIGl0DQpwb3N0Z3Jlcy5pZHggPC0gZGJHZXRRdWVyeShjb24sICdTRUxFQ1QgKiBGUk9NIHlvdXJ2aWV3L3NlbGVjdCBzdGF0ZW1lbnQnKQ0KbGVmdF9qb2luKG15LmlkeCwgcG9zdGdyZXMuaWR4LCBieT1jKCdtZXRhX2lkJywgJ21ldGFfaWQnKSkgJT4lDQogIFZpZXcoKQ0KYGBgDQoNCiMgNi4gU3BhdGlhbCBhbmFseXNpcw0KDQojIyA2LjEJRmluZCBzcGF0aWFsIGRhdGEgDQo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj5Zb3UgZG9uJ3QgbmVlZCBTUUwgZm9yIHRoaXMgdGFzay4gQ2hlY2sgb3V0IHRoZSB0YWJsZSBuYW1lcyBhbmQgZG8gc29tZSByZXNlYXJjaCBvbmxpbmUgdG8gZmluZCBvdXQgbW9yZSBhYm91dCB0aGUgZGF0YSB1c2VkLjwvZGl2Pg0KDQpUaGUgZGF0YSBjb21lcyBmcm9tIE9wZW5TdHJlZXRNYXAsIHRoZSBsYXJnZXN0IGNvbW11bml0eSBkcml2ZW4gZWZmb3J0IHRvIGJ1aWxkIGEgZGF0YWJhc2Ugb2YgZ2xvYmFsIHN0cnVjdHVyYWwgZ2VvZGF0YSwgd2hpY2ggY2FuIGJlIHVzZWQgYnkgZXZlcnlvbmUgdW5kZXIgYSBPRGJMIGxpY2Vuc2UsIHdoaWNoIGluY2x1ZGVzIGNvbW1lcmNpYWwgdXNlcy4gR2VvZGF0YSBzdGFuZGFyZHMsIGxpa2UgdGhlIE9TTSBtb2RlbCBidXQgYWxzbyBFdXJvcGVhbiBzdGFuZGFyZHMgbGlrZSBJTlNQSVJFIGFyZSBleHRyZW1lbHkgaW1wb3J0YW50IHRvIG1ha2UgcHVibGljIGdlb2RhdGEgdXNhYmxlLiBUaGVzZSBkYXRhc2V0cyBhcmUgaGVhdmlseSBzdHJ1Y3R1cmVkIGFuZCB0aGVpciB1c2Ugb2Z0ZW4gaW52b2x2ZXMgbWFueSBkaW1lbnNpb25zIGFuZCBsYXJnZSBkYXRhIGFtb3VudHMuIFdpdGhvdXQgYSBzdGFuZGFyZCwgZWFjaCBhdXRob3JpdHkgd291bGQgcHVibGlzaCBkaWZmZXJlbnQgZGF0YSBhbmQgYSBjb2xsZWN0aXZlIHVzZSBpcyBpbiBmYWN0IGltcG9zc2libGUuDQoNCiMjIDYuMglGaWx0ZXIgYnkgbG9jYXRpb24NCg0KPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+DQpGaWx0ZXIgdGhlIGRhdGFiYXNlIGZvciBvbmx5IHRoZSBjaXR5IGRpc3RyaWN0cywgdGhhdCBjb250YWluIGEgcmVmZXJlbmNlIHN0YXRpb24gYW5kIHByZXNlbnQgdGhlbSBpbiBlaXRoZXIgaW4gYSBodW1hbiByZWFkYWJsZSB0YWJsZSBvciBhIG1hcC4NCjwvZGl2Pg0KDQpGaXJzdCwgY3JlYXRlIGEgc3ViLXF1ZXJ5L3ZpZXcvd2l0aCBzdGF0ZW1lbnQgY29udGFpbmluZyBhbGwgcmVmZXJlbmNlIHN0YXRpb25zLiBUaGVuIGV4dGVudCB0aGlzIHdpdGggdGhlIG5lZWRlZCBmaWx0ZXINCmBgYHtzcWwgY29ubmVjdGlvbj1jb259DQpjcmVhdGUgdGVtcG9yYXJ5IHZpZXcgc29sdXRpb25fNjIgYXMNCndpdGggcmVmIGFzICgNCiAgc2VsZWN0ICdEV0QgMTQ0MycgYXMgbmFtZSwgX19fIGFzIGdlb20gdW5pb24NCiAgc2VsZWN0ICdEV0QgMTM2NjcnIGFzIG5hbWUsIF9fXyAgYXMgZ2VvbSB1bmlvbg0KICBzZWxlY3QgJ1VuaSBGUiBNZXRlbycgYXMgbmFtZSwgX19fICBhcyBnZW9tIHVuaW9uDQogIHNlbGVjdCAnV0JJJyBhcyBuYW1lLCBfX18gYXMgZ2VvbQ0KKSwNCmRpc3RyaWN0cyBhcyAoDQogIHNlbGVjdCBuYW1lLCBnZW9tIGZyb20gb3NtX25vZGVzIHdoZXJlIG5vZGVfdHlwZT0nZGlzdHJpY3QnDQopDQoNClNFTEVDVCBkLm5hbWUgYXMgZGlzdHJpY3QsIHJlZi5uYW1lIGFzICJyZWZlcmVuY2Ugc3RhdGlvbiIsIGQuZ2VvbSBhcyBnZW9tZXRyeSANCkZST00gX19fDQpXSEVSRSBfX18NCmBgYA0KYGBge3J9DQojIFRoaXMgaXMganVzdCBhIHN1Z2dlc3Rpb24NCmRpc3QgPC0gcmVhZF9zZihjb24sIHF1ZXJ5PSJTRUxFQ1QgKiBmcm9tIHNvbHV0aW9uXzYyIiwgcXVpZXQ9VCkNCnBsb3RfbHkoKSAlPiUgDQogIGFkZF9zZihkYXRhPWRpc3QsIC4uLikgJT4lDQogICAgbGF5b3V0KA0KICAgICAgbWFwYm94PWxpc3Qoc3R5bGU9InN0YW1lbi10ZXJyYWluIiwgem9vbT0xMC4sIGNlbnRlcj1saXN0KGxvbj03LjgsIGxhdD00OCksIHBpdGNoPTE1KSwgDQogICAgICBsZWdlbmQ9bGlzdChvcmllbnRhdGlvbj0naCcpKQ0KYGBgDQoNCg0KIyMgNi4zCUFnZ3JlZ2F0ZSBieSBsb2NhdGlvbiANCg0KPGRpdiBjbGFzcz0iYWxlcnQgYWxlcnQtaW5mbyI+DQpRdWVyeSBhbGwgY2l0eSBkaXN0cmljdHMgb2YgRnJlaWJ1cmcgdGhhdCBjb250YWluIGF0IGxlYXN0IHRocmVlIEhPQk9zIGFuZCBhZ2dyZWdhdGUgdGhlIGluZGljZXMgY2FsY3VsYXRlZCBpbiAqQ3JlYXRlIGluZGljZXMgdGFibGUvdmlldy9xdWVyeSogZm9yIGVhY2ggb2YgdGhlc2UgZGlzdHJpY3RzIGFuZCBwcmVzZW50IHRoZW0gYXMgYSB0YWJsZS4NClJlcGVhdCB0aGUgcHJvY2VkdXJlIGZvciB0aGUgSE9CTyBsb2NhdGlvbnMgb2YgV1QyMSBvciBXVDIyIChvciBib3RoKS4gQXJlIHRoZXJlIGRpZmZlcmVuY2VzPyBEZXNjcmliZS4NCjwvZGl2Pg0KDQoNCmBgYHtzcWwgY29ubmVjdGlvbj1jb259DQp3aXRoIGRpc3RyaWN0cyBhcyAoDQogIHNlbGVjdCBuLm5hbWUsIGNvdW50KG0uKikgYXMgaG9ib3MsIG4uZ2VvbSBmcm9tIG1ldGFkYXRhIG0gDQogIGpvaW4gb3NtX25vZGVzIG4gb24gDQogIHdoZXJlIHRlcm1faWQgPSA0MiBhbmQgDQogIGdyb3VwIGJ5IG4ubmFtZSwgbi5nZW9tDQopDQpzZWxlY3QgKiBmcm9tIGRpc3RyaWN0cyANCndoZXJlIGhvYm9zID49IDQyDQpvcmRlciBieSBob2JvcyBkZXNjDQpgYGANCg0KDQojIyA2LjQJQ3JlYXRpbmcgYSBtYXAgDQoNCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPg0KVXNlIHRoZSBxdWVyaWVzIGNvbnN0cnVjdGVkIGluIHRoZSBsYXN0IHRhc2sgdG8gY3JlYXRlIGEgbWFwIG9mIEZyZWlidXJnLCB0aGF0IGlsbHVzdHJhdGVzIGRpZmZlcmVuY2VzIGluIG9uZSAob3IgbW9yZSkgb2YgdGhlIHRlbXBlcmF0dXJlIGluZGljZXMgYmV0d2VlbiB0aGUgY2l0eSBkaXN0cmljdHMgb2YgRnJlaWJ1cmcuIFlvdSBjYW4gY3JlYXRlIHRoaXMgbWFwIGVpdGhlciBpbiBSIG9yIGluIFFHaXMuDQo8L2Rpdj4NCg0KR2V0IHRoZSBkYXRhIGhlcmU6DQpgYGB7c3FsIGNvbm5lY3Rpb249Y29ufQ0KDQpgYGANCg0KRWl0aGVyIGluY2x1ZGUgdGhlIG1hcCBvciBtYWtlIHRoZSB2aXN1YWxpemF0aW9uIGhlcmU6DQpgYGB7cn0NCg0KYGBgDQoNCg0KIyBjbGVhbnVwDQpgYGB7cn0NCmRiRGlzY29ubmVjdChjb24pDQpgYGANCg==